﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Xml.Linq;
using YawetagLib.Utilities;
using ExpressionVisitor = YawetagLib.Linq.Expressions.ExpressionVisitor;

namespace YawetagLib.Data.Xml
{
    internal class EntityToXElementModifier : ExpressionVisitor
    {
        public Expression Modify(Expression expression)
        {
            return Visit(expression);
        }

        protected override Expression VisitLambda(LambdaExpression lambda)
        {
            XElementParameter = Expression.Parameter(typeof(XElement), lambda.Parameters[0].Name);
            Expression body = Visit(lambda.Body);

            return Expression.Lambda(body, XElementParameter);
        }

        protected override Expression VisitMemberAccess(MemberExpression m)
        {
            return IsPropertyAccess(m) 
                ? GetXExpression(m) 
                : base.VisitMemberAccess(m);
        }

        private static bool IsPropertyAccess(MemberExpression m)
        {
            return m.Expression != null
                && m.Expression.NodeType == ExpressionType.Parameter
                && m.Member.MemberType == MemberTypes.Property;
        }

        private Expression GetXExpression(MemberExpression m)
        {
            string propertyName = m.Member.Name;
            Type propertyType = m.Member.GetAt<Type>("PropertyType");

            MethodInfo methodInfo = GetAttibuteAtMethodInfo().MakeGenericMethod(propertyType);
            ConstantExpression attributeNameParameter = Expression.Constant(propertyName);

            return Expression.Call(methodInfo, XElementParameter, attributeNameParameter);
        }

        private static MethodInfo GetAttibuteAtMethodInfo()
        {
            return typeof(Extensions).GetMethods().Where(i => i.Name == "GetAttibuteAt" && i.IsGenericMethod).Single();
        }

        private ParameterExpression XElementParameter { get; set; }
    }
}
